// input: go run main.go -pattern="hello" -fileFilter="1.txt" -onlyMatching -ignoreCase -lineNumbers -out="output.json"
// output: 1.txt:1: hello
package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"sync"
)

// SearchInfo 搜索代码搜索信息
type SearchInfo struct {
	Directory      string `json:"directory"`
	Pattern        string `json:"pattern"`
	FileFilter     string `json:"fileFilter"`
	IgnoreCase     bool   `json:"ignoreCase"`
	IncludeLineNum bool   `json:"includeLineNumbers"`
	ExecutionTime  string `json:"executionTime"`
}

// SearchResult 搜索特定结果
type SearchResult struct {
	Filename string   `json:"filename"`
	Matches  []string `json:"matches"`
	Lines    []string `json:"lines"`
	LineNums []int    `json:"lineNumbers,omitempty"`
}

// re 确保正则表达式包含全局匹配标志
func re(pattern string, ignoreCase bool) *regexp.Regexp {
	var flag string
	if ignoreCase {
		flag = "(?i)"
	}
	return regexp.MustCompile(flag + pattern)
}

// createSearchInfoMessage 创建搜索信息
func createSearchInfoMessage(directory, pattern, fileFilter string, ignoreCase, includeLineNumbers bool) string {
	return fmt.Sprintf(
		"// 定义信息\n// 目录: %s\n// 模式: %s\n// 文件过滤器: %s\n// 是否区分大小写: %s\n// 是否包括行号: %s",
		directory,
		pattern,
		fileFilter,
		!ignoreCase,
		includeLineNumbers,
	)
}

// formatResultsToJson 将结果格式化为美观的JSON
func formatResultsToJson(results []SearchResult, searchInfo string) string {
	jsonData, _ := json.MarshalIndent(map[string]interface{}{
		"searchInfo": searchInfo,
		"results":    results,
	}, "", "  ")
	return string(jsonData)
}

// searchContent 搜索按钮功能，搜索文件内容并匹配正则表达式
func searchContent(filename string, pattern *regexp.Regexp, includeLineNumbers bool) (SearchResult, bool) {
	file, err := os.Open(filename)
	if err != nil {
		return SearchResult{}, false
	}
	defer file.Close()

	var result SearchResult
	result.Filename = filename

	scanner := bufio.NewScanner(file)
	lineNumber := 1

	for scanner.Scan() {
		line := scanner.Text()
		matches := pattern.FindAllString(line, -1)
		if len(matches) > 0 {
			result.Matches = append(result.Matches, matches...)
			result.Lines = append(result.Lines, line)
			if includeLineNumbers {
				result.LineNums = append(result.LineNums, lineNumber)
			}
		}
		lineNumber++
	}

	if err := scanner.Err(); err != nil {
		return SearchResult{}, false
	}

	return result, len(result.Matches) > 0
}

// find 主函数：在目录中查找匹配的文件并搜索内容
func find(directory, pattern, fileFilter string, options struct {
	includeLineNumbers bool
	outputFile         string
	ignoreCase         bool
}) []SearchResult {
	var wg sync.WaitGroup
	// var mu sync.Mutex

	files, _ := filepath.Glob(filepath.Join(directory, fileFilter))

	var results []SearchResult
	var muResults sync.Mutex

	for _, file := range files {
		wg.Add(1)
		go func(file string) {
			defer wg.Done()

			result, ok := searchContent(file, re(pattern, options.ignoreCase), options.includeLineNumbers)
			if ok {
				muResults.Lock()
				results = append(results, result)
				muResults.Unlock()
			}
		}(file)
	}

	wg.Wait()

	if options.outputFile != "" {
		searchInfo := createSearchInfoMessage(
			directory,
			pattern,
			fileFilter,
			options.ignoreCase,
			options.includeLineNumbers,
		)

		jsonData := formatResultsToJson(results, searchInfo)

		err := os.WriteFile(options.outputFile, []byte(jsonData), 0644)
		if err != nil {
			fmt.Println("无法写入输出文件:", err)
		}
	}

	return results
}

// printResults 根据参数决定输出内容
func printResults(results []SearchResult, onlyMatching, lineNumbers bool) {
	for _, r := range results {
		var arr []string
		if onlyMatching {
			arr = r.Matches
		} else {
			arr = r.Lines
		}

		for i, element := range arr {
			if lineNumbers && len(r.LineNums) > 0 {
				fmt.Printf("%s:%d: %s\n", r.Filename, r.LineNums[i], element)
			} else {
				fmt.Println(element)
			}
		}
	}
}

func main() {
	pattern := flag.String("pattern", "", "指定要搜索的模式（支持正则表达式）")
	fileFilter := flag.String("fileFilter", "*", "指定要搜索的文件过滤模式（支持glob模式）")
	onlyMatching := flag.Bool("onlyMatching", false, "只输出匹配的文本内容")
	ignoreCase := flag.Bool("ignoreCase", false, "忽略大小写")
	lineNumbers := flag.Bool("lineNumbers", false, "显示匹配内容的行号")
	outputFile := flag.String("out", "", "指定输出结果的文件路径")
	helpFlag := flag.Bool("help", false, "显示帮助信息")

	flag.Parse()

	if *helpFlag {
		flag.Usage()
		return
	}

	if *pattern == "" {
		fmt.Print("Input complex pattern: ")
		var input string
		fmt.Scanln(&input)
		*pattern = input
	}

	results := find(
		".", // 当前目录
		*pattern,
		*fileFilter,
		struct {
			includeLineNumbers bool
			outputFile         string
			ignoreCase         bool
		}{
			includeLineNumbers: *lineNumbers,
			outputFile:         *outputFile,
			ignoreCase:         *ignoreCase,
		},
	)

	printResults(results, *onlyMatching, *lineNumbers)
}
